﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using Medianamik.Core.Bootstrapping;
using Medianamik.Tests.Common;
using Medianamik.Tests.Performance;
using NUnit.Framework;
using System.Globalization;

namespace Medianamik.Core
{
    [TestFixture]
    public class NodeManagerTest
    {
        private static readonly Guid SimpleTypeId = new Guid("fc046a24-1ce7-43cb-9ad6-6b1cb7cc1f72");
        private static readonly Guid ComplexTypeId = new Guid("60f43c3a-8222-416d-8c2f-356c7db45935");
        private static readonly Guid SimpleNodeId = new Guid("2ab6949c-5e73-45ed-b8c6-c305bb31d2b6");
        private static readonly Guid ComplexNodeId = new Guid("c5630b2a-6717-4c07-951b-0b4185b51a84");
        private static readonly CultureInfo Culture = new CultureInfo("fr-CA");
        private static readonly Stopwatch TimeWatcher = new Stopwatch();
        private const int NumberOfInsertions = 100;
        private static readonly PerformanceBootstapper Bootstrapper = new PerformanceBootstapper();

        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {

        }

        [SetUp]
        public void SetUp()
        {
            Utilities.RestoreDatabase("RestoreCS");
 
            //Ensure caches are loaded before test execution

            var i = 0;

            while (i < 5)
            {
                try
                {
                    Bootstrapper.RegisterActionHandlers();
                    var type = NodeTypeManager.GetNodeType(SimpleTypeId);
                    var nodeId = SaveNode(ComplexTypeId);
                    QueryNode(nodeId);
                    nodeId = SaveNode(SimpleTypeId);
                    QueryNode(nodeId);
                    TimeWatcher.Reset();
                    i = 5;
                }
                catch (Exception)
                {
                    //System.Threading.Thread.Sleep(5000);
                    i++;
                }
            }
        }

        [TestFixtureTearDown]
        public void TestFixtureTearDown()
        {
            Utilities.RestoreDatabase("RestoreCS");
        }

        [TearDown]
        public void TearDown()
        {
            TimeWatcher.Reset();
        }

        [Test]
        public void Time_required_for_saving_one_complex_node()
        {
            SaveNode(ComplexTypeId);

            Assert.That(TimeWatcher.ElapsedMilliseconds, Is.LessThan(1));
        }

        [Test]
        public void Time_required_for_saving_one_simple_node()
        {
            SaveNode(SimpleTypeId);

            Assert.That(TimeWatcher.ElapsedMilliseconds, Is.LessThan(1));
        }

        [Test]
        public void Average_times_required_for_saving_100_complex_nodes_then_query_all()
        {
            SaveAllThenQueryAll(ComplexTypeId);
        }

        [Test]
        public void Average_times_required_for_saving_100_simple_nodes_then_query_all()
        {
            SaveAllThenQueryAll(SimpleTypeId);
        }

        private void SaveAllThenQueryAll(Guid typeId)
        {
            var nodeIds = new List<Guid>();
            var timeWatcher = new Stopwatch();

            for (var i = 0; i < NumberOfInsertions; i++)
                nodeIds.Add(SaveNode(typeId));

            for (var i = 0; i < NumberOfInsertions; i++)
                QueryNode(nodeIds[i], timeWatcher);

            Assert.That("Write: " + (TimeWatcher.ElapsedMilliseconds / NumberOfInsertions) + " - Read: " +
                        (timeWatcher.ElapsedMilliseconds / NumberOfInsertions), Is.EqualTo(String.Empty));
        }

        [Test]
        public void Time_required_for_querying_one_complex_node()
        {
            QueryNode(ComplexNodeId);

            Assert.That(TimeWatcher.ElapsedMilliseconds, Is.LessThan(1));
        }

        [Test]
        public void Time_required_for_querying_one_simple_node()
        {
            QueryNode(SimpleNodeId);

            Assert.That(TimeWatcher.ElapsedMilliseconds, Is.LessThan(1));
        }

        [Test]
        public void Average_times_required_for_alernating_save_and_query_for_100_complex_nodes()
        {
            SaveAndQueryAll(ComplexTypeId);
        }

        [Test]
        public void Average_times_required_for_alernating_save_and_query_for_100_simple_nodes()
        {
            SaveAndQueryAll(SimpleTypeId);
        }

        private void SaveAndQueryAll(Guid typeId)
        {
            var timeWatcher = new Stopwatch();

            for (var i = 0; i < NumberOfInsertions; i++)
            {
                var nodeId = SaveNode(typeId);
                QueryNode(nodeId, timeWatcher);
            }

            Assert.That("Write: " + (TimeWatcher.ElapsedMilliseconds / NumberOfInsertions) + " - Read: " +
                        (timeWatcher.ElapsedMilliseconds / NumberOfInsertions), Is.EqualTo(String.Empty));
        }

        private Guid SaveNode(Guid typeId)
        {
            var node = NodeManager.NewNode(typeId, Culture);
            node.DefaultCulture = Culture;
            node.DefaultInstance.Properties["Name"].SetValue("SimpleNode");

            TimeWatcher.Start();
            NodeManager.SaveNode(node);
            TimeWatcher.Stop();

            return node.ID;
        }

        private void QueryNode(Guid nodeId)
        {
            QueryNode(nodeId, TimeWatcher);
        }

        private void QueryNode(Guid nodeId, Stopwatch timeWatcher)
        {
            timeWatcher.Start();
            NodeManager.GetNode(nodeId);
            timeWatcher.Stop();
        }
    }
}
